/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * http//www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is ART OWL API.
 *
 * The Initial Developer of the Original Code is University of Roma Tor Vergata.
 * Portions created by University of Roma Tor Vergata are Copyright (C) 2009.
 * All Rights Reserved.
 *
 * The ART OWL API were developed by the Artificial Intelligence Research Group
 * (art.uniroma2.it) at the University of Roma Tor Vergata
 * Current information about the ART OWL API can be obtained at 
 * http://art.uniroma2.it/owlart
 *
 */

package it.uniroma2.art.owlart.protegeimpl.models;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.exceptions.ModelCreationException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.exceptions.UnsupportedRDFFormatException;
import it.uniroma2.art.owlart.io.RDFFormat;
import it.uniroma2.art.owlart.model.ARTResource;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.model.NodeFilters;
import it.uniroma2.art.owlart.models.BaseRDFTripleModel;
import it.uniroma2.art.owlart.models.ModelFactory;
import it.uniroma2.art.owlart.models.OWLArtModelFactory;
import it.uniroma2.art.owlart.models.OWLModel;
import it.uniroma2.art.owlart.models.RDFModel;
import it.uniroma2.art.owlart.navigation.ARTNodeIterator;
import it.uniroma2.art.owlart.navigation.ARTResourceIterator;
import it.uniroma2.art.owlart.navigation.ARTStatementIterator;
import it.uniroma2.art.owlart.protegeimpl.models.conf.ProtegeModelConfiguration;
import it.uniroma2.art.owlart.protegeimpl.models.conf.ProtegeProjectModelConfiguration;
import it.uniroma2.art.owlart.testutils.AssertOntologies;
import it.uniroma2.art.owlart.vocabulary.OWL;
import it.uniroma2.art.owlart.vocabulary.RDF;
import it.uniroma2.art.owlart.vocabulary.RDFS;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

import org.junit.Test;

/**
 * this UnitTest cannot be run on its own. It needs a specific OWLArt API Implementation declaring a
 * <code>@BeforeClass</code> annotated method which invokes the {@link #initializeTest(ModelFactory)} defined
 * in this class. This way, all the test code written here can be reused in all future Model Implementations
 * <p>Note that the hasStatement method is currently available only when manipulating automatically produced
 * statements. It is currently not possible to produce <code>Statement</code> instances through the API.
 * </p>
 * 
 * @author Armando Stellato <stellato@info.uniroma2.it>
 * 
 */
public abstract class BaseRDFModelTestReadOnly {
	
	static final String baseURI = "http://pippo.it";
	static final String resourcesFolder = "src/test/resources";
	static public final String testRepoFolder = resourcesFolder + "/testRepo";
	static final String owlARTAPIResourcesFolder = "./../OWLArtAPI/src/test/resources";
	static final String importsFolder = owlARTAPIResourcesFolder + "/imports";
	
	static final String foafURIString = "http://xmlns.com/foaf/0.1/";
	static final String foafFile = "foaf_20071102.rdf";
	static OWLModel model;

	static ARTResource localObjectA;
	static ARTResource localObjectB;
	static ARTResource localClassA;
	static ARTResource localClassB;
	static ARTResource localClassSubB;
	static ARTURIResource objectProperty1;
	static ARTURIResource datatypeProperty1;

	/**
	 * this method should be marked as <code>@BeforeClass</code>, however, since BeforeClass and
	 * <code>@AfterClass</code> methods are defined to be static, we left invocation to the BeforeClass method
	 * of specific test units from OWL ART API Implementations
	 * 
	 * @param factImpl
	 * @throws Exception
	 */
	public static BaseRDFTripleModel initializeTest(ModelFactory factImpl) throws Exception {
		ModelFactory fact = OWLArtModelFactory.createModelFactory(factImpl);
		model = null;
		try {
			System.out.println("generating model");
			ProtegeProjectModelConfiguration conf = new ProtegeProjectModelConfiguration();
			conf.projectFileName = "test";
			model = fact.loadOWLModel(baseURI, testRepoFolder, conf);
			initializeResources();
		} catch (ModelCreationException e) {
			e.printStackTrace();
		}
		return model;
	}

	public static void initializeResources() {
		String testNameSpace = "http://art.uniroma2.it/ontologies/test.owl#";
		localObjectA = model.createURIResource(testNameSpace + "localObjectA");
		localObjectB = model.createURIResource(testNameSpace + "localObjectB");
		localClassA = model.createURIResource(testNameSpace + "localClassA");
		localClassB = model.createURIResource(testNameSpace + "localClassB");
		localClassSubB = model.createURIResource(testNameSpace + "localClassSubB");
		objectProperty1 = model.createURIResource(testNameSpace + "objectProperty1");
		datatypeProperty1 = model.createURIResource(testNameSpace + "datatypeProperty1");

		// this is because the baseRDFModel loading process does not load any RDF vocabulary
		RDF.Res.TYPE = model.createURIResource(RDF.TYPE);
		RDFS.Res.LABEL = model.createURIResource(RDFS.LABEL);
	}

	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#hasTriple(ARTResource, ARTURIResource, ARTNode, boolean, ARTResource...)</li>
	 * </ul>
	 */
	@Test
	public void testHasTriplePropertyValue() {
		try {
			assertTrue(model.hasTriple(localObjectB, objectProperty1, localObjectA, false));
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("failed model access on testHasTriple");
			ae.initCause(e);
			throw ae;
		}
	}
	
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#hasTriple(ARTResource, ARTURIResource, ARTNode, boolean, ARTResource...)</li>
	 * </ul>
	 */
	@Test
	public void testHasTripleRDFType() {
		try {
			assertTrue(model.hasTriple(localObjectA, RDF.Res.TYPE, localClassA, false));
			assertFalse(model.hasTriple(localObjectA, RDF.Res.TYPE, localClassB, false));
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("failed model access on testHasTriple");
			ae.initCause(e);
			throw ae;
		}
	}
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#listStatements(ARTResource, ARTURIResource, it.uniroma2.art.owlart.model.ARTNode, boolean, ARTResource...)</li>
	 * </ul>
	 */
	@Test
	public void testListStatements() {
		ARTStatementIterator it;
		try {
			it = model.listStatements(localClassSubB, NodeFilters.ANY, NodeFilters.ANY, false);
			ArrayList<ARTResource> expectedResources = new ArrayList<ARTResource>();
			ArrayList<ARTResource> retrievedResources = new ArrayList<ARTResource>();
			expectedResources.add(OWL.Res.CLASS);
			expectedResources.add(localClassB);
			while (it.streamOpen())
				retrievedResources.add(it.getNext().getObject().asResource());
			AssertOntologies.assertSameUnorderedCollections(expectedResources, retrievedResources);
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("failed model access on testHasTriple");
			ae.initCause(e);
			throw ae;
		}
	}
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link RDFModel#listSubjectsOfPredObjPair(ARTURIResource, it.uniroma2.art.owlart.model.ARTNode, boolean, ARTResource...)}</li>
	 * </ul>
	 */
	@Test
	public void testListSubjectsOfPredObjPair() {
		ARTResourceIterator it;
		try {
			it = model.listSubjectsOfPredObjPair(objectProperty1, localObjectA, false);
			if (it.streamOpen()) {
				ARTResource subject = it.getNext();
				assertEquals(localObjectB, subject);
			}				
			else
				assertTrue(false);
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("failed model access on testHasTriple");
			ae.initCause(e);
			throw ae;
		}
	}
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link RDFModel#listValuesOfSubjPredPair(ARTResource, ARTURIResource, boolean, ARTResource...)}</li>
	 * </ul>
	 */
	@Test
	public void testListValuesOfSubjPredPair() {
		ARTNodeIterator it;
		try {
			it = model.listValuesOfSubjPredPair(localObjectB, RDF.Res.TYPE, false);
			if (it.streamOpen()) {
				ARTResource value = it.getNext().asResource();
				assertEquals(localClassB, value);
			}				
			else
				assertTrue(false);
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("failed model access on testHasTriple");
			ae.initCause(e);
			throw ae;
		}
	}
		
	/**
	 * <p>Methods involved in this test:</p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#setDefaultNamespace(String)</li>
	 * <li>{@link BaseRDFTripleModel#getDefaultNamespace()</li>
	 * </ul>
	 */
	@Test
	public void testSetDefaultNamespace() {
		try {
			String oldDefNameSpace = model.getDefaultNamespace();
			model.setDefaultNamespace("http://starred/newnamespace#");
			assertEquals("http://starred/newnamespace#", model.getDefaultNamespace());
			model.setDefaultNamespace(oldDefNameSpace);
		} catch (ModelUpdateException e) {
			AssertionError ae = new AssertionError("failed model access on "
					+ "testSetNamespace");
			ae.initCause(e);
			throw ae;
		}
	}
	
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#setBaseURI(String)</li>
	 * <li>{@link BaseRDFTripleModel#getBaseURI()</li>
	 * </ul>
	 */
	@Test
	public void testSetBaseURI() {
		try {
			String oldBaseURI = model.getBaseURI();
			model.setDefaultNamespace("http://starred/newBaseURI");
			assertEquals("http://starred/newBaseURI", model.getDefaultNamespace());
			model.setDefaultNamespace(oldBaseURI);
		} catch (ModelUpdateException e) {
			AssertionError ae = new AssertionError("failed model access on "
					+ "testSetBaseURI");
			ae.initCause(e);
			throw ae;
		}
	}
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#addRDF(File, String, RDFFormat, ARTResource...)</li>
	 * <li>{@link BaseRDFTripleModel#hasTriple(ARTResource, ARTURIResource, ARTNode, boolean, ARTResource...)</li>
	 * <li>{@link BaseRDFTripleModel#deleteTriple(ARTResource, ARTURIResource, ARTNode, ARTResource...)</li>
	 * </ul>
	 */
	
	/*
	@Test
	public void testAddRDFAndListNamespaces() {
		File inputFile = new File(importsFolder, foafFile);
		ARTURIResource foafURI = model.createURIResource(foafURIString);
		try {
			//testAddRDF
			model.addRDF(inputFile, foafURIString, RDFFormat.RDFXML, foafURI);
			assertTrue(model.hasTriple(model.createURIResource(foafURIString+"Document"), RDFS.Res.SUBCLASSOF, model.createURIResource("http://xmlns.com/wordnet/1.6/Document"), false, foafURI));
			
			//testListNamespaces
			Collection<String> expectedNamespacesCollection = new ArrayList<String>();
			Collection<String> actualNamespacesCollection = new ArrayList<String>();
			expectedNamespacesCollection.add("http://purl.org/dc/elements/1.1/");
			expectedNamespacesCollection.add("http://xmlns.com/wot/0.1/");
			expectedNamespacesCollection.add("http://www.w3.org/2000/01/rdf-schema#");
			expectedNamespacesCollection.add("http://xmlns.com/foaf/0.1/");
			expectedNamespacesCollection.add("http://www.w3.org/2002/07/owl#");
			expectedNamespacesCollection.add("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
			expectedNamespacesCollection.add("http://www.w3.org/2003/06/sw-vocab-status/ns#");
						
			ARTNamespaceIterator nsit = model.listNamespaces();
			while(nsit.streamOpen())
				actualNamespacesCollection.add(nsit.getNext().getName());
			nsit.close();			
			assertSameUnorderedCollections(expectedNamespacesCollection, actualNamespacesCollection);

			//testListNamedGraphs
			Collection<ARTResource> expectedNamedGraphsCollection = new ArrayList<ARTResource>();
			Collection<ARTResource> actualNamedGraphsCollection = new ArrayList<ARTResource>();
			expectedNamedGraphsCollection.add(foafURI);
			ARTResourceIterator resIt = model.listNamedGraphs();
			Iterators.addAll(actualNamedGraphsCollection, resIt);
			resIt.close();			
			assertSameUnorderedCollections(expectedNamedGraphsCollection, actualNamedGraphsCollection);
			
			//cleaning the triples and checking their existence after removal
			model.deleteTriple(NodeFilters.ANY, NodeFilters.ANY, NodeFilters.ANY, foafURI);
			assertFalse(model.hasTriple(NodeFilters.ANY, NodeFilters.ANY, NodeFilters.ANY, false, foafURI));
			
			//testListNamedGraphs after removing all triples from foaf
			actualNamedGraphsCollection.clear();
			resIt = model.listNamedGraphs();
			Iterators.addAll(actualNamedGraphsCollection, resIt);
			resIt.close();			
			expectedNamedGraphsCollection.remove(foafURI); //FOAF should be no more present after removal of its triples
			assertSameUnorderedCollections(expectedNamedGraphsCollection, actualNamedGraphsCollection);
		} catch (FileNotFoundException e) {
			AssertionError ae = new AssertionError("file not found on testAddRDF");	ae.initCause(e); throw ae;
		} catch (IOException e) {
			AssertionError ae = new AssertionError("io exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("access exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (ModelUpdateException e) {
			AssertionError ae = new AssertionError("update exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (UnsupportedRDFFormatException e) {
			AssertionError ae = new AssertionError("unsupported rdf format on testAddRDF");
			ae.initCause(e); throw ae;
		}
	}
*/
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#addRDF(URL, String, RDFFormat, ARTResource...)</li>
	 * <li>{@link BaseRDFTripleModel#hasTriple(ARTResource, ARTURIResource, ARTNode, boolean, ARTResource...)</li>
	 * <li>{@link BaseRDFTripleModel#deleteTriple(ARTResource, ARTURIResource, ARTNode, ARTResource...)</li>
	 * </ul>
	 */
	/*
	@Test
	public void testAddRDFFromURL() {
		File inputFile = new File(importsFolder, foafFile);		
		ARTURIResource foafURI = model.createURIResource(foafURIString);
		try {
			URL url = inputFile.toURI().toURL();
			model.addRDF(url, foafURIString, RDFFormat.RDFXML, foafURI);
			assertTrue(model.hasTriple(model.createURIResource(foafURIString+"Document"), RDFS.Res.SUBCLASSOF, model.createURIResource("http://xmlns.com/wordnet/1.6/Document"), false, foafURI));
			model.deleteTriple(NodeFilters.ANY, NodeFilters.ANY, NodeFilters.ANY, foafURI);
			assertFalse(model.hasTriple(NodeFilters.ANY, NodeFilters.ANY, NodeFilters.ANY, false, foafURI));
		} catch (FileNotFoundException e) {
			AssertionError ae = new AssertionError("file not found on testAddRDF");	ae.initCause(e); throw ae;
		} catch (IOException e) {
			AssertionError ae = new AssertionError("io exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("access exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (ModelUpdateException e) {
			AssertionError ae = new AssertionError("update exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (UnsupportedRDFFormatException e) {
			AssertionError ae = new AssertionError("unsupported rdf format on testAddRDF");
			ae.initCause(e); throw ae;
		}
	}
	*/
	
	/**
	 * <p>
	 * Methods involved in this test:
	 * </p>
	 * <ul>
	 * <li>{@link BaseRDFTripleModel#writeRDF(File, RDFFormat, ARTResource...)</li>
	 * </ul>
	 */
	@Test
	public void testWriteRDF() {
		File outputFile = new File(owlARTAPIResourcesFolder, "output.rdf");		
		try {
			if ( outputFile.exists() )
				outputFile.delete();
			model.writeRDF(outputFile, RDFFormat.NTRIPLES);
			assertTrue(outputFile.exists());
			//outputFile.deleteOnExit();
		} catch (FileNotFoundException e) {
			AssertionError ae = new AssertionError("file not found on testAddRDF");	ae.initCause(e); throw ae;
		} catch (IOException e) {
			AssertionError ae = new AssertionError("io exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (ModelAccessException e) {
			AssertionError ae = new AssertionError("access exception on testAddRDF");	ae.initCause(e); throw ae;
		} catch (UnsupportedRDFFormatException e) {
			AssertionError ae = new AssertionError("unsupported rdf format on testAddRDF");
			ae.initCause(e); throw ae;
		}
	}
	
	
	/**
	 * this method should be marked as <code>@AfterClass</code>, however, since AfterClass and
	 * <code>@BeforeClass</code> methods are defined to be static, we left invocation to the AfterClass method
	 * of specific test units from OWL ART API Implementations.
	 * 
	 * This is just part of code which can be reused across different implementation (just closing the model)
	 * specific implementations should take charge of removing the persistence stuff
	 * 
	 * @param factImpl
	 * @throws Exception
	 */
	public static void closeRepository() throws Exception {
		System.out.println("-- tearin' down the test --");
		model.close();
		System.out.println("model closed");
		System.out.println("now specific implementation should remove persistence data...");
	}

}
